// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// This file implements Authorization Server Metadata.
// See https://www.rfc-editor.org/rfc/rfc8414.html.

//go:build mcp_go_client_oauth

package oauthex

import (
	"context"
	"errors"
	"fmt"
	"net/http"
)

// AuthServerMeta represents the metadata for an OAuth 2.0 authorization server,
// as defined in [RFC 8414].
//
// Not supported:
// - signed metadata
//
// [RFC 8414]: https://tools.ietf.org/html/rfc8414)
type AuthServerMeta struct {
	// GENERATED BY GEMINI 2.5.

	// Issuer is the REQUIRED URL identifying the authorization server.
	Issuer string `json:"issuer"`

	// AuthorizationEndpoint is the REQUIRED URL of the server's OAuth 2.0 authorization endpoint.
	AuthorizationEndpoint string `json:"authorization_endpoint"`

	// TokenEndpoint is the REQUIRED URL of the server's OAuth 2.0 token endpoint.
	TokenEndpoint string `json:"token_endpoint"`

	// JWKSURI is the REQUIRED URL of the server's JSON Web Key Set [JWK] document.
	JWKSURI string `json:"jwks_uri"`

	// RegistrationEndpoint is the RECOMMENDED URL of the server's OAuth 2.0 Dynamic Client Registration endpoint.
	RegistrationEndpoint string `json:"registration_endpoint,omitempty"`

	// ScopesSupported is a RECOMMENDED JSON array of strings containing a list of the OAuth 2.0
	// "scope" values that this server supports.
	ScopesSupported []string `json:"scopes_supported,omitempty"`

	// ResponseTypesSupported is a REQUIRED JSON array of strings containing a list of the OAuth 2.0
	// "response_type" values that this server supports.
	ResponseTypesSupported []string `json:"response_types_supported"`

	// ResponseModesSupported is a RECOMMENDED JSON array of strings containing a list of the OAuth 2.0
	// "response_mode" values that this server supports.
	ResponseModesSupported []string `json:"response_modes_supported,omitempty"`

	// GrantTypesSupported is a RECOMMENDED JSON array of strings containing a list of the OAuth 2.0
	// grant type values that this server supports.
	GrantTypesSupported []string `json:"grant_types_supported,omitempty"`

	// TokenEndpointAuthMethodsSupported is a RECOMMENDED JSON array of strings containing a list of
	// client authentication methods supported by this token endpoint.
	TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`

	// TokenEndpointAuthSigningAlgValuesSupported is a RECOMMENDED JSON array of strings containing
	// a list of the JWS signing algorithms ("alg" values) supported by the token endpoint for
	// the signature on the JWT used to authenticate the client.
	TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"`

	// ServiceDocumentation is a RECOMMENDED URL of a page containing human-readable documentation
	// for the service.
	ServiceDocumentation string `json:"service_documentation,omitempty"`

	// UILocalesSupported is a RECOMMENDED JSON array of strings representing supported
	// BCP47 [RFC5646] language tag values for display in the user interface.
	UILocalesSupported []string `json:"ui_locales_supported,omitempty"`

	// OpPolicyURI is a RECOMMENDED URL that the server provides to the person registering
	// the client to read about the server's operator policies.
	OpPolicyURI string `json:"op_policy_uri,omitempty"`

	// OpTOSURI is a RECOMMENDED URL that the server provides to the person registering the
	// client to read about the server's terms of service.
	OpTOSURI string `json:"op_tos_uri,omitempty"`

	// RevocationEndpoint is a RECOMMENDED URL of the server's OAuth 2.0 revocation endpoint.
	RevocationEndpoint string `json:"revocation_endpoint,omitempty"`

	// RevocationEndpointAuthMethodsSupported is a RECOMMENDED JSON array of strings containing
	// a list of client authentication methods supported by this revocation endpoint.
	RevocationEndpointAuthMethodsSupported []string `json:"revocation_endpoint_auth_methods_supported,omitempty"`

	// RevocationEndpointAuthSigningAlgValuesSupported is a RECOMMENDED JSON array of strings
	// containing a list of the JWS signing algorithms ("alg" values) supported by the revocation
	// endpoint for the signature on the JWT used to authenticate the client.
	RevocationEndpointAuthSigningAlgValuesSupported []string `json:"revocation_endpoint_auth_signing_alg_values_supported,omitempty"`

	// IntrospectionEndpoint is a RECOMMENDED URL of the server's OAuth 2.0 introspection endpoint.
	IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`

	// IntrospectionEndpointAuthMethodsSupported is a RECOMMENDED JSON array of strings containing
	// a list of client authentication methods supported by this introspection endpoint.
	IntrospectionEndpointAuthMethodsSupported []string `json:"introspection_endpoint_auth_methods_supported,omitempty"`

	// IntrospectionEndpointAuthSigningAlgValuesSupported is a RECOMMENDED JSON array of strings
	// containing a list of the JWS signing algorithms ("alg" values) supported by the introspection
	// endpoint for the signature on the JWT used to authenticate the client.
	IntrospectionEndpointAuthSigningAlgValuesSupported []string `json:"introspection_endpoint_auth_signing_alg_values_supported,omitempty"`

	// CodeChallengeMethodsSupported is a RECOMMENDED JSON array of strings containing a list of
	// PKCE code challenge methods supported by this authorization server.
	CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
}

var wellKnownPaths = []string{
	"/.well-known/oauth-authorization-server",
	"/.well-known/openid-configuration",
}

// GetAuthServerMeta issues a GET request to retrieve authorization server metadata
// from an OAuth authorization server with the given issuerURL.
//
// It follows [RFC 8414]:
//   - The well-known paths specified there are inserted into the URL's path, one at time.
//     The first to succeed is used.
//   - The Issuer field is checked against issuerURL.
//
// [RFC 8414]: https://tools.ietf.org/html/rfc8414
func GetAuthServerMeta(ctx context.Context, issuerURL string, c *http.Client) (*AuthServerMeta, error) {
	var errs []error
	for _, p := range wellKnownPaths {
		u, err := prependToPath(issuerURL, p)
		if err != nil {
			// issuerURL is bad; no point in continuing.
			return nil, err
		}
		asm, err := getJSON[AuthServerMeta](ctx, c, u, 1<<20)
		if err == nil {
			if asm.Issuer != issuerURL { // section 3.3
				// Security violation; don't keep trying.
				return nil, fmt.Errorf("metadata issuer %q does not match issuer URL %q", asm.Issuer, issuerURL)
			}

			if len(asm.CodeChallengeMethodsSupported) == 0 {
				return nil, fmt.Errorf("authorization server at %s does not implement PKCE", issuerURL)
			}

			return asm, nil
		}
		errs = append(errs, err)
	}
	return nil, fmt.Errorf("failed to get auth server metadata from %q: %w", issuerURL, errors.Join(errs...))
}
